package com.hospital.pet.love.lovepethospital.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hospital.pet.love.lovepethospital.domain.*;
import com.hospital.pet.love.lovepethospital.dto.caze.*;
import com.hospital.pet.love.lovepethospital.dto.register.GetRegisterDto;
import com.hospital.pet.love.lovepethospital.exp.ShawCustomException;
import com.hospital.pet.love.lovepethospital.mapper.*;
import com.hospital.pet.love.lovepethospital.resp.ResultCodeEnum;
import com.hospital.pet.love.lovepethospital.service.CaseService;
import com.hospital.pet.love.lovepethospital.service.RegisterService;
import com.hospital.pet.love.lovepethospital.util.AuthenticationContextUtil;
import com.hospital.pet.love.lovepethospital.util.RoleJudgeUtil;
import com.hospital.pet.love.lovepethospital.vo.caze.CaseExcelVo;
import com.hospital.pet.love.lovepethospital.vo.caze.CaseVo;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author RoselleShaw
 * @description 针对表【case】的数据库操作Service实现
 * @createDate 2024-04-26 15:59:22
 */
@Service
public class CaseServiceImpl extends ServiceImpl<CaseMapper, Case>
        implements CaseService {

    @Resource
    private CaseMapper caseMapper;
    @Resource
    private RegisterService registerService;
    @Resource
    private EmployeeMapper employeeMapper;
    @Resource
    private DrugMapper drugMapper;
    @Resource
    private CaseDrugMapper caseDrugMapper;
    @Resource
    private PetInfoMapper petInfoMapper;

    @Override
    public List<CaseVo> getConsumer(GetCaseConsumerDto getCaseConsumerDto) {
        RoleJudgeUtil.consumerJudge();
        GetRegisterDto getRegisterDto = new GetRegisterDto();
        getRegisterDto.setDoctorName(getCaseConsumerDto.getDoctorName());
        getRegisterDto.setPetName(getCaseConsumerDto.getPetName());
        getRegisterDto.setState(1);
        List<Register> registers = registerService.getByPhone(getRegisterDto);
        List<Case> cases = new ArrayList<>();
        if (!CollectionUtils.isEmpty(registers)) {
            LambdaQueryWrapper<Case> wrapper = new LambdaQueryWrapper<>();
            if (getCaseConsumerDto.getStart() != null) {
                wrapper.ge(Case::getCaseTime, getCaseConsumerDto.getStart());
            }
            if (getCaseConsumerDto.getEnd() != null) {
                wrapper.le(Case::getCaseTime, getCaseConsumerDto.getEnd());
            }
            registers.forEach(register -> cases.add(caseMapper.selectOne(
                    wrapper.eq(Case::getRegisterId, register.getRegisterId())
            )));
        }
        return cases.stream().map(caze -> {
            CaseVo caseVo = new CaseVo();
            BeanUtil.copyProperties(caze, caseVo);
            Map<Integer, Integer> medications = new HashMap<>();
            List<CaseDrug> caseDrugs = caseDrugMapper.selectList(new LambdaQueryWrapper<CaseDrug>()
                    .eq(CaseDrug::getCaseId, caseVo.getCaseId())
            );
            caseDrugs.forEach(caseDrug -> medications.put(caseDrug.getDrugId(), caseDrug.getDrugQuantity()));
            caseVo.setMedications(medications);
            return caseVo;
        }).toList();
    }

    @Override
    public List<CaseVo> getEmployee(GetCaseEmployeeDto getCaseEmployeeDto) {
        RoleJudgeUtil.employeeJudge();
        LambdaQueryWrapper<PetInfo> petInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (getCaseEmployeeDto.getPhone() != null) {
            petInfoLambdaQueryWrapper.eq(PetInfo::getPhone, getCaseEmployeeDto.getPhone());
        }
        if (getCaseEmployeeDto.getPetName() != null) {
            petInfoLambdaQueryWrapper.eq(PetInfo::getName, getCaseEmployeeDto.getPetName());
        }
        List<PetInfo> petInfos = petInfoMapper.selectList(petInfoLambdaQueryWrapper);
        List<Register> registers = new ArrayList<>();
        petInfos.forEach(petInfo -> registers.addAll(registerService.list(new LambdaQueryWrapper<Register>().eq(Register::getPetId, petInfo.getPetId()))));
        List<Case> cases = registers.stream().map(register -> caseMapper.selectOne(new LambdaQueryWrapper<Case>().eq(Case::getRegisterId, register.getRegisterId()))).toList();
        return cases.stream().map(caze -> {
            CaseVo caseVo = new CaseVo();
            BeanUtil.copyProperties(caze, caseVo);
            Map<Integer, Integer> medications = new HashMap<>();
            List<CaseDrug> caseDrugs = caseDrugMapper.selectList(new LambdaQueryWrapper<CaseDrug>()
                    .eq(CaseDrug::getCaseId, caseVo.getCaseId())
            );
            caseDrugs.forEach(caseDrug -> medications.put(caseDrug.getDrugId(), caseDrug.getDrugQuantity()));
            caseVo.setMedications(medications);
            return caseVo;
        }).toList();
    }

    @Override
    public void exportData(HttpServletResponse response, CaseExportDto caseExportDto) {
        RoleJudgeUtil.employeeJudge();
        try {
            // 1. 设置响应头信息和其他信息
            // 设置响应结果类型
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            // 这里 URLEncoder.encode 可以防止中文乱码，当然和 EasyExcel 没有关系
            String fileName = URLEncoder.encode("病例报表", StandardCharsets.UTF_8);
            // 设置响应头信息
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");

            // 2. 查询所有分类
            LambdaQueryWrapper<Case> caseLambdaQueryWrapper = new LambdaQueryWrapper<>();
            if (caseExportDto.getBegin() != null) {
                caseLambdaQueryWrapper.ge(Case::getCaseTime, caseExportDto.getBegin());
            }
            if (caseExportDto.getEnd() != null) {
                caseLambdaQueryWrapper.le(Case::getCaseTime, caseExportDto.getEnd());
            }
            if (caseExportDto.getState() != null) {
                if (caseExportDto.getState() != 0 && caseExportDto.getState() != 1 && caseExportDto.getState() != 2) {
                    throw new ShawCustomException(ResultCodeEnum.CASE_STATUS_ERROR);
                }
                caseLambdaQueryWrapper.eq(Case::getVisitState, caseExportDto.getState());
            }
            List<Case> list = caseMapper.selectList(caseLambdaQueryWrapper);

            List<CaseExcelVo> excelVoList = new ArrayList<>();
            for (Case caze : list) {
                CaseExcelVo caseExcelVo = new CaseExcelVo();
                BeanUtil.copyProperties(caze, caseExcelVo);
                caseExcelVo.setPayState(caze.getPayState() == 0 ? "未支付" : "已支付");
                caseExcelVo.setVisitState(caze.getVisitState() == 0 ? "就诊" : (caze.getVisitState() == 1 ? "支付完成" : "取消"));
                excelVoList.add(caseExcelVo);
            }

            // 3. 调用 EasyExcel 的 write 方法完成写操作
            EasyExcel.write(response.getOutputStream(), CaseExcelVo.class)
                    .sheet("病例数据").doWrite(excelVoList);
        } catch (IOException e) {
            throw new ShawCustomException(ResultCodeEnum.DATA_ERROR);
        }
    }

    @Override
    public void add(AddCaseDto addCaseDto) {
        // 判断是否员工
        RoleJudgeUtil.employeeJudge();

        // 判断是否医生
        if (!"医生".equals(employeeMapper.selectOne(
                new LambdaQueryWrapper<Employee>().eq(Employee::getPhone, AuthenticationContextUtil.get().getPhone())
        ).getJob())) {
            throw new ShawCustomException(ResultCodeEnum.NOT_DOCTOR);
        }

        // 判断是否存在挂号
        if (registerService.getById(addCaseDto.getRegisterId()) == null) {
            throw new ShawCustomException(ResultCodeEnum.REGISTER_NOT_EXIST);
        }

        // 新增病例
        Case caze = new Case();
        caze.setRegisterId(addCaseDto.getRegisterId());
        caze.setDiseaseDescription(addCaseDto.getDiseaseDescription());
        caze.setDiagnosis(addCaseDto.getDiagnosis());
        caze.setTreatment(addCaseDto.getTreatment());
        caze.setPayMoney(addCaseDto.getPayMoney());
        caseMapper.insert(caze);

        // 新增药品使用记录
        addCaseDto.getMedications().forEach((key, value) -> {
            CaseDrug caseDrug = new CaseDrug();
            caseDrug.setCaseId(caze.getCaseId());
            caseDrug.setState(0);
            caseDrug.setDrugId(key);
            caseDrug.setDrugQuantity(value);
            caseDrugMapper.insert(caseDrug);
        });

        // 修改挂号情况
        Register register = new Register();
        register.setRegisterId(addCaseDto.getRegisterId());
        register.setState(1);
        registerService.updateById(register);
    }

    @Override
    public void updateById(UpdateCaseDto updateCaseDto) {
        // 判断是否员工
        RoleJudgeUtil.employeeJudge();

        // 判断是否医生
        if (!"医生".equals(employeeMapper.selectOne(
                new LambdaQueryWrapper<Employee>().eq(Employee::getPhone, AuthenticationContextUtil.get().getPhone())
        ).getJob())) {
            throw new ShawCustomException(ResultCodeEnum.NOT_DOCTOR);
        }

        if (caseMapper.selectById(updateCaseDto.getCaseId()) == null) {
            throw new ShawCustomException(ResultCodeEnum.CASE_NOT_EXIST);
        }

        // 修改病例
        Case caze = new Case();
        caze.setCaseId(updateCaseDto.getCaseId());
        caze.setDiseaseDescription(updateCaseDto.getDiseaseDescription());
        caze.setDiagnosis(updateCaseDto.getDiagnosis());
        caze.setTreatment(updateCaseDto.getTreatment());
        caze.setPayMoney(updateCaseDto.getPayMoney());
        if (updateCaseDto.getVisitState() != 0 && updateCaseDto.getVisitState() != 2) {
            throw new ShawCustomException(ResultCodeEnum.CASE_STATUS_ERROR);
        }
        caze.setVisitState(updateCaseDto.getVisitState());
        caseMapper.updateById(caze);

        // 新增药品使用记录
        Map<Integer, Integer> medications = updateCaseDto.getMedications();
        if (medications != null) {
            caseDrugMapper.delete(new LambdaQueryWrapper<CaseDrug>()
                    .eq(CaseDrug::getCaseId, updateCaseDto.getCaseId())
            );
            medications.forEach((key, value) -> {
                CaseDrug caseDrug = new CaseDrug();
                caseDrug.setCaseId(caze.getCaseId());
                caseDrug.setState(0);
                caseDrug.setDrugId(key);
                caseDrug.setDrugQuantity(value);
                caseDrugMapper.insert(caseDrug);
            });
        }
    }

    @Override
    public void pay(Integer id) {
        // 判断是否员工
        RoleJudgeUtil.employeeJudge();
        Case caze = caseMapper.selectById(id);
        if (caze == null) {
            throw new ShawCustomException(ResultCodeEnum.CASE_NOT_EXIST);
        }
        if (caze.getPayState() != 0) {
            throw new ShawCustomException(ResultCodeEnum.PAID_OR_CANCELED);
        }

        // 判断药品是否存在和库存是否充足，记录药品金额
        BigDecimal bigDecimal = new BigDecimal("0");

        List<CaseDrug> caseDrugs = caseDrugMapper.selectList(new LambdaQueryWrapper<CaseDrug>()
                .eq(CaseDrug::getCaseId, id)
        );
        for (CaseDrug caseDrug : caseDrugs) {
            Drug drug = drugMapper.selectById(caseDrug.getDrugId());
            if (drug == null) {
                throw new ShawCustomException(ResultCodeEnum.DRUG_NOT_EXIST);
            }
            if (drug.getDrugQuantity() < caseDrug.getDrugQuantity()) {
                throw new ShawCustomException(216, drug.getDrugName() + "不足");
            }
            bigDecimal = bigDecimal.add(drug.getDrugPrice().multiply(new BigDecimal(caseDrug.getDrugQuantity())));
        }

        // 判断金额是否大于等于药品金额
        BigDecimal payMoney = caze.getPayMoney();
        if (payMoney.compareTo(bigDecimal) < 0) {
            throw new ShawCustomException(ResultCodeEnum.MONEY_SHORT);
        }

        for (CaseDrug caseDrug : caseDrugs) {
            Drug drug = drugMapper.selectById(caseDrug.getDrugId());
            drug.setDrugQuantity(drug.getDrugQuantity() - caseDrug.getDrugQuantity());
            drugMapper.updateById(drug);
            caseDrug.setState(1);
            caseDrugMapper.updateById(caseDrug);
        }

        caze.setPayState(1);
        caze.setVisitState(1);
        caseMapper.updateById(caze);
    }
}